package jblip.gui;

import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIManager.LookAndFeelInfo;

import jblip.base.HttpClient;
import jblip.base.resources.DefaultResourceFactory;
import jblip.gui.components.ChannelFrame;
import jblip.gui.components.LoginFrame;
import jblip.gui.components.WaitFrame;
import jblip.gui.data.DatabaseClient;
import jblip.gui.data.images.IconCache;
import jblip.gui.data.images.ImageCache;

public class JBlip {
  private static JBlip JBLIP;
  private ChannelFrame channelFrame;
  
  public ChannelFrame getChannelFrame() {
	  return channelFrame;
  }

  public static void main(String[] args) throws Throwable {
    JBLIP = new JBlip(getHomeDir());
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      @Override
      public void run() {
        // this should really prevent it from being called by gc, but forces to
        // be run on exit :)
        JBLIP.finalize();
      }
    }));

    JBLIP.login();
    JBLIP.initDatabase();
    JBLIP.showChannels();
  }

  private static File getHomeDir() throws IOException {
    final String env_home = System.getenv("JBLIP_HOME");
    if (env_home != null) {
      final File env_dir = new File(env_home);
      if ((env_dir.isDirectory() && env_dir.canRead()) || !env_dir.exists()) {
        return env_dir;
      }
      System.err.println("ENV_HOME points to invalid directory");
    }
    return new File(Configuration.getDefaultJBlipDirectory());
  }

  public static JBlip instance() {
    return JBLIP;
  }

  private final Configuration configuration;

  private JBlipClient client = null;

  private LoginFrame login_frame = null;

  private final File home;

  private final File cache_home;

  private ImageCache img_cache;

  private IconCache icon_cache;

  private JBlip(final File home_dir) throws FileNotFoundException, IOException {
    final File cache_dir =
        new File(home_dir.getPath() + File.separator + "imgcache");
    final File icon_cache_dir =
        new File(cache_dir.getPath() + File.separator + "avatars");
    createDir(home_dir);
    createDir(cache_dir);
    createDir(icon_cache_dir);

    this.home = home_dir;
    this.cache_home = cache_dir;
    this.configuration =
        new Configuration(new File(home_dir.getPath() + File.separator
            + "config"));
  }

  private void createDir(final File dir) {
    if (!dir.exists()) {
      dir.mkdir();
    } else if (!dir.isDirectory() || !dir.canRead()) {
      System.err.println("Can't read from " + dir.getPath());
      System.exit(2);
    }
  }

  private void login() throws InterruptedException, InvocationTargetException {
    if (configuration.hasLogin() && configuration.hasPassword()) {
      init(configuration.getLogin(), configuration.getPassword());
    }

    if (client == null) {
      SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
          login_frame = new LoginFrame();
        }
      });
    }

    while (client == null) {
      synchronized (this) {
        wait();
        init(login_frame.getUser(), login_frame.getPass());
        notify();
      }
    }
  }

  private void initDatabase() {
    final String user = client.getLoggedUserName();
    final String pass = client.getLoggedUserPassword();
    DatabaseClient db = null;
    while (db == null) {
      db = DatabaseClient.init(user, pass);
      if (db == null) {
        String old_pass = showReinitPane();
        if (old_pass == null) {
          System.exit(0);
        } else {
          DatabaseClient.reinit(user, old_pass, pass);
        }
      }
    }
    this.client.setCache(db);
  }

  private String showReinitPane() {
    final StringBuilder pass = new StringBuilder();
    final String info =
        "Nie można otworzyć bazy danych. Prawdopodobnie zmieniłaś/-eś "
            + "hasło nie korzystając z JBlipa. Aby kontynuować wpisz swoje stare hasło. "
            + "Jeżeli go nie pamiętasz, jedynym wyjściem jest usunięcie plików bazy "
            + "z katalogu domowego JBlipa (nazwy plików zaczynają się Twoim loginem).";

    try {
      SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
          final JPasswordField passwd = new JPasswordField();
          final JOptionPane pane =
              new JOptionPane(new Object[] { info, passwd },
                  JOptionPane.WARNING_MESSAGE, JOptionPane.OK_CANCEL_OPTION,
                  null, new String[] { "OK", "Anuluj" });
          final JDialog dialog = new JDialog();
          dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
          dialog.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentShown(ComponentEvent e) {
              passwd.requestFocusInWindow();
            }
          });
          pane.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
              final String prop = evt.getPropertyName();
              if (dialog.isVisible()
                  && (JOptionPane.VALUE_PROPERTY.equals(prop) || JOptionPane.INPUT_VALUE_PROPERTY
                      .equals(prop))) {
                final Object value = pane.getValue();
                if (value == JOptionPane.UNINITIALIZED_VALUE) {
                  return;
                }
                // reset button property
                pane.setValue(JOptionPane.UNINITIALIZED_VALUE);
                if ("OK".equals(value)) {
                  pass.append(passwd.getPassword());
                  if (pass.length() == 0) {
                    JOptionPane.showMessageDialog(
                        dialog, "Podaj hasło lub anuluj.");
                    passwd.requestFocusInWindow();
                  }
                }
                dialog.dispose();
              }
            }
          });

        }
      });
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }

    if (pass.length() == 0) {
      return null;
    } else {
      return toString();
    }
  }

  @Override
  protected void finalize() {
    try {
      configuration.store();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public Configuration getConfiguration() {
    return configuration;
  }

  public HttpClient getBlipClient() {
    return client;
  }

  public File getHomeDirectory() {
    return home;
  }

  public ImageCache getImageCache() {
    return img_cache;
  }

  public IconCache getIconCache() {
    return icon_cache;
  }

  public boolean init(final String user, final String pass)
      throws InterruptedException, InvocationTargetException {
    configuration.setCredentials(user, pass);

    final Thread init_task = new Thread(new Runnable() {
      @Override
      public void run() {
        JBlipClient testclient = new JBlipClient(user, pass);
        if (testclient.testLogin()) {
          testclient.setResourcesFactory(new DefaultResourceFactory());
          JBlip.this.setClient(testclient);
          JBlip.this.img_cache = new ImageCache(cache_home);
          JBlip.this.icon_cache = new IconCache(cache_home);
        } else {
          // System.err.println(testclient.getLastError().toString());
        }
      }
    });

    SwingUtilities.invokeAndWait(new Runnable() {
      @Override
      public void run() {
        WaitFrame wait_frame =
            new WaitFrame("Logowanie", "Loguję do blipa, cierpliwości");
        wait_frame.runTask(init_task);
      }
    });

    init_task.join();

    return client != null;
  }

  private void showChannels() throws InterruptedException,
      InvocationTargetException {
    SwingUtilities.invokeAndWait(new Runnable() {

	@Override
      public void run() {
        try {
          UIManager
              .setLookAndFeel("org.jvnet.substance.skin.SubstanceRavenGraphiteGlassLookAndFeel");
        } catch (Exception e) {
          System.err.println("Failed to initialize Substance L&F");
          LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
          if (lafs.length < 1) {
//        	  no LAFs installed?
	          System.err.println("Failed to initialize L&F (no L&F installed)");
	          System.exit(199);
          }
          try {
        	  String usedLAFName = lafs[0].getClassName();
        	  System.err.println("Using L&F: " + usedLAFName);
        	  UIManager.setLookAndFeel(usedLAFName);
          } catch (ClassNotFoundException e1) {
        	  System.err.println("Failed to initialize L&F (class not found)");
        	  System.exit(200);
          } catch (InstantiationException e1) {
        	  System.err.println("Failed to initialize L&F (instantiation exception)");
        	  System.exit(200);
          } catch (IllegalAccessException e1) {
        	  System.err.println("Failed to initialize L&F (illegal acces)");
        	  System.exit(200);
          } catch (UnsupportedLookAndFeelException e1) {
        	  System.err.println("Failed to initialize L&F (unsupported l&f)");
        	  System.exit(200);
          }
          // boo, we look crappy :(
        }
        channelFrame = new ChannelFrame();
      }
    });
  }

  private void setClient(JBlipClient newclient) {
    this.client = newclient;
  }
}